//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <winhack.h>
#include <elasysapi.h>
#include <sdl.h>
#include "video.h"

#define CheckMemoryBuf(pBuf, type)  \
                 ((NULL == pBuf)    \
                  || (pBuf->GetCapacity() < (Int32)(sizeof(type))))

CVideo::~CVideo()
{
    if (!m_shadow.IsEmpty()) {
        m_pShadowManager->RemoveShadow(&m_shadow);
    }
    // todo; event dispatcher thread.
    if (NULL != m_hDispatcherThread) {
        m_dispatcher.RequestToQuit();
        CloseHandle(m_hDispatcherThread);
        m_hDispatcherThread = NULL;
    }
    if (NULL != m_hSyncEvent) {
        WaitForSingleObject(m_hSyncEvent, (DWORD)INFINITE);
        CloseHandle(m_hSyncEvent);
        m_hSyncEvent = NULL;
    }
}

DWORD __stdcall SDLThreadRoutine(void * pArgs)
{
    CVideo * pThis = (CVideo *)pArgs;
    pThis->EventDispatchLoop();

    return 0;
}

void CVideo::EventDispatchLoop()
{
    m_ecode = m_context.Initialize();

    /*
     * to notify video to conitinue initialization.
     */
    SetEvent(m_hSyncEvent);

    if (FAILED(m_ecode)) {
        return;
    }

    m_dispatcher.Loop();

    /*
     * to notify video to continue destruction
     */
    SetEvent(m_hSyncEvent);
}

ECode CVideo::Initialize()
{
    DWORD threadId;

    m_pShadowManager = GetShadowManager();
    m_pShadowManager->AddShadow(&m_shadow);

    m_hSyncEvent = CreateEventW(NULL, FALSE, 0, NULL);
    if (NULL == m_hSyncEvent) {
        return E_FAIL;
    }
    m_hDispatcherThread = CreateThread(NULL,
                                0,
                                SDLThreadRoutine,
                                (void *)this,
                                0,
                                (LPDWORD)&threadId);
    if (NULL == m_hDispatcherThread) {
        return E_FAIL;
    }

    WaitForSingleObject(m_hSyncEvent, (DWORD)INFINITE);
    if (FAILED(m_ecode)) {
        return m_ecode;
    }

    return NOERROR;
}

ECode CVideo::Read(
    /* [in] */ Int64 offset,
    /* [in] */ Int32 unumberOfBytesToRead,
    /* [out] */ MemoryBuf * pData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    // TODO;
    return E_NOT_IMPLEMENTED;
}

ECode CVideo::Write(
    /* [in] */ Int64 offset,
    /* [in] */ const MemoryBuf & ebbData,
    /* [out] */ Int32 * pNumberOfBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    // TODO;
    return E_NOT_IMPLEMENTED;
}

EXTERN bool IsSimulator();
ECode CVideo::Control(
    /* [in] */ Handle32 controlCode,
    /* [in] */ const MemoryBuf & ebbInData,
    /* [out] */ MemoryBuf * pOutData,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = NOERROR;

    switch (controlCode) {
        case 0:
            m_context.ClearBaseplane();
            break;
        case 1:
        // get physical address to the frame buffer.
        // m_pContext->GetPhysicalBuffer(pOutData);
            break;
        case 2:
            break;
        case 3:
            Address address;
            UInt32 id;

            if (CheckMemoryBuf(pOutData, Address)) {
                return E_INVALID_ARGUMENT;
            }

            if (IsSimulator()) {
                ec = m_context.StartDrawing();
                if (FAILED(ec)) {
                    break;
                }
            }
            m_context.GetBaseplaneAddress(&address, &id);
            *(Address *)pOutData->GetPayload() = address;
            break;

        case 6:
            break;
        case 7:
            FrameBufferFormat fbFormat;

            if (CheckMemoryBuf(pOutData, FrameBufferFormat)) {
                return E_INVALID_ARGUMENT;
            }

            m_context.GetBaseplaneFormat(&fbFormat);
            pOutData->Copy((Byte *)&fbFormat, sizeof(FrameBufferFormat));

            break;
        case 11:
            m_context.Render();
            if(ebbInData.IsNullOrEmpty()) {
                m_context.UpdateToSurface();
            }
            else {
                RenderFrame* frame = (RenderFrame*)ebbInData.GetPayload();
                m_context.UpdateToSurface(frame->startX,frame->startY,
                         frame->srcBuffer.width,frame->srcBuffer.height);
            }
            break;

        case 12:
            m_context.AutoRender();
            break;

        default:
            break;
    }

    return ec;
}

void CVideo::Dispose()
{
    delete this;
}
